#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdint.h>

#include <pthread.h>
#include <errno.h>

#include "share_mem.h"
#include "clog_log.h"

static shared_mem_t* g_shared_mem = NULL;

int32_t shmem_ring_init(const char* shm_name, size_t shm_size)
{
    int32_t ret = 0;
    int32_t is_new = 0;
    int32_t shm_fd = -1;
    shared_mem_t *shared_mem = NULL;
    pthread_mutexattr_t mutex_attr;
    pthread_condattr_t cond_attr;
    struct stat shm_stat;

    shm_fd = shm_open(shm_name, O_RDWR, 0666);
    if (shm_fd >= 0) {
        if (fstat(shm_fd, &shm_stat) < 0) {
            clog_log_error("fstat failed\n");
            close(shm_fd);
            return -1;
        }
        if(shm_stat.st_size != shm_size) {
            clog_log_error("Shared memory size mismatch (existing: %ld, expected: %zu)\n", 
                    shm_stat.st_size, shm_size);
            close(shm_fd);
            return -1;
        }
    } else {
        if (errno != ENOENT) {
            clog_log_error("shm_open (existing) failed, errno: %d\n", errno);
            return -1;
        }
        shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666);
        if (shm_fd < 0) {
            clog_log_error("shm_open (create) failed\n");
            return -1;
        }
        is_new = 1;

        if (ftruncate(shm_fd, shm_size) < 0) {
            clog_log_error("ftruncate failed\n");
            close(shm_fd);
            shm_unlink(shm_name);
            return -1;
        }
    }

    shared_mem = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shared_mem == MAP_FAILED) {
        clog_log_error("mmap failed\n");
        close(shm_fd);
        if (is_new) {
            shm_unlink(shm_name);
        }
        return -1;
    }
    close(shm_fd);

    /* if shm is not init, init it */
    if (is_new) {
        /* init shared mem */
        strcpy(shared_mem->name, shm_name);
        shared_mem->capacity = shm_size - sizeof(shared_mem_t);
        shared_mem->head = 0;
        shared_mem->tail = 0;
        shared_mem->used_size = 0;
        memset((uint8_t*)shared_mem + sizeof(shared_mem_t), 0, shared_mem->capacity);

        /* init shared mem mutex */
        if (pthread_mutexattr_init(&mutex_attr) != 0) {
            clog_log_error("pthread_mutexattr_init failed\n");
            return -1;
        }

        if (pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED) != 0) {
            clog_log_error("pthread_mutexattr_setpshared failed\n");
            pthread_mutexattr_destroy(&mutex_attr);
            return -1;
        }

        if (pthread_mutex_init(&shared_mem->mutex, &mutex_attr) != 0) {
            clog_log_error("pthread_mutex_init failed\n");
            pthread_mutexattr_destroy(&mutex_attr);
            return -1;
        }
        /* destroy shared mem mutex attr */
        pthread_mutexattr_destroy(&mutex_attr);
        

        /* init shared mem cond */
        if (pthread_condattr_init(&cond_attr) != 0) {
            clog_log_error("pthread_condattr_init failed\n");
            return -1;
        }

        if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0) {
            clog_log_error("pthread_condattr_setpshared failed\n");
            pthread_condattr_destroy(&cond_attr);
            return -1;
        }

        if (pthread_cond_init(&shared_mem->cond, &cond_attr) != 0) {
            clog_log_error("pthread_cond_init failed\n");
            pthread_condattr_destroy(&cond_attr);
            return -1;
        }
        /* destroy shared mem cond attr */
        pthread_condattr_destroy(&cond_attr);
    } else {
        /* Check if existing shared memory name matches expected name */
        if(strcmp(shared_mem->name, shm_name) != 0) {
            clog_log_error("shmem ring name mismatch (existing: %s, expected: %s)\n", 
                    shared_mem->name, shm_name);
            munmap(shared_mem, shm_size);
            return -1;
        }
    }

    clog_log_info("shmem_ring_init success, shm_name: %s, shm_size: %zu\n", shm_name, shm_size);

    g_shared_mem = shared_mem;
    return 0;
}

int32_t shmem_ring_destroy(const char* shm_name)
{
    int32_t ret = 0;
    size_t shm_size = 0;
    if (g_shared_mem == NULL) {
        clog_log_error("shared mem is NULL\n");
        return -1;
    }
    /* destroy shared mem cond */
    pthread_cond_destroy(&g_shared_mem->cond);

    /* destroy shared mem mutex */
    pthread_mutex_destroy(&g_shared_mem->mutex);

    /* unlink shared mem */
    ret = shm_unlink(shm_name);
    if (ret < 0) {
        clog_log_error("shm_unlink failed, ret: %d", ret);
        return -1;
    }

    /* destroy shared mem */
    shm_size = g_shared_mem->capacity + sizeof(shared_mem_t);
    ret = munmap(g_shared_mem, shm_size);
    if (ret < 0) {
        clog_log_error("munmap failed");
        return -1;
    }

    g_shared_mem = NULL;
    return 0;
}

int32_t slave_get_shared_mem(shared_mem_t** shared_mem, const char* shm_name)
{
    int32_t ret = 0;
    int shm_fd = 0;
    struct stat shm_stat;

    if (shared_mem == NULL) {
        clog_log_error("shared_mem is NULL");
        return -1;
    }

    shm_fd = shm_open(shm_name, O_RDWR, 0666);
    if (shm_fd < 0) {
        clog_log_error("shm_open failed, errno: %d", errno);
        return -1;
    }

    if (fstat(shm_fd, &shm_stat) < 0) {
        clog_log_error("fstat failed\n");
        close(shm_fd);
        return -1;
    }

    shared_mem_t* tmp_shmem = mmap(NULL, shm_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (tmp_shmem == MAP_FAILED) {
        clog_log_error("mmap failed\n");
        close(shm_fd);
        return -1;
    }

    if ((uint8_t*)tmp_shmem + sizeof(shared_mem_t) + tmp_shmem->capacity > 
        (uint8_t*)tmp_shmem + shm_stat.st_size) {
        clog_log_error("shared memory data area overflow, capacity: %zu, shm_size: %ld\n",
                      tmp_shmem->capacity, shm_stat.st_size);
        munmap(tmp_shmem, shm_stat.st_size);
        close(shm_fd);
        return -1;
    }

    *shared_mem = tmp_shmem;
    close(shm_fd);

    return 0;
}

int32_t shmem_ring_enqueue(shared_mem_t* shared_mem, const uint8_t* data, size_t len)
{
    uint8_t* shm_data_area = NULL;
    struct timespec timeout;
    if (shared_mem == NULL || data == NULL || len == 0) {
        clog_log_error("invalid parameters");
        return -1;
    }

    
    clock_gettime(CLOCK_REALTIME, &timeout);
    timeout.tv_sec += 1;

    if(pthread_mutex_timedlock(&shared_mem->mutex, &timeout) != 0) {
        clog_log_error("pthread_mutex_lock timeout (1s), possible deadlock or permanently locked mutex\n");
        return -1;
    }

    if (shared_mem->used_size + len > shared_mem->capacity) {
        clog_log_error("shared memory full, len: %zu, used: %zu, capacity: %zu\n",
                len, shared_mem->used_size, shared_mem->capacity);
        pthread_mutex_unlock(&shared_mem->mutex);
        return -1;
    }

    shm_data_area = (uint8_t*)shared_mem + sizeof(shared_mem_t);
    if (shared_mem->tail + len <= shared_mem->capacity) {
        memcpy(shm_data_area + shared_mem->tail, data, len);
        shared_mem->tail += len;
    } else {
        size_t first_part = shared_mem->capacity - shared_mem->tail;
        memcpy(shm_data_area + shared_mem->tail, data, first_part);
        memcpy(shm_data_area, data + first_part, len - first_part);
        shared_mem->tail = len - first_part;
    }

    shared_mem->used_size += len;
    pthread_mutex_unlock(&shared_mem->mutex);
    pthread_cond_signal(&shared_mem->cond);

    return 0;
}

int32_t shmem_ring_dequeue(uint8_t* data, size_t* data_len)
{
    uint8_t* shm_data_area = NULL;
    int32_t mem_used_size = 0;
    if (g_shared_mem == NULL || data == NULL) {
        clog_log_error("invalid parameters\n");
        return -1;
    }

    if (pthread_mutex_lock(&g_shared_mem->mutex) != 0) {
        clog_log_error("pthread_mutex_lock failed\n");
        return -1;
    }

    while (g_shared_mem->used_size == 0) {
        pthread_cond_wait(&g_shared_mem->cond, &g_shared_mem->mutex);
    }

    mem_used_size = g_shared_mem->used_size;
    shm_data_area = (uint8_t*)g_shared_mem + sizeof(shared_mem_t);
    
    if (g_shared_mem->head + mem_used_size <= g_shared_mem->capacity) {
        memcpy(data, shm_data_area + g_shared_mem->head, mem_used_size);
        g_shared_mem->head += mem_used_size;
    } else {
        size_t first_part = g_shared_mem->capacity - g_shared_mem->head;
        memcpy(data, shm_data_area + g_shared_mem->head, first_part);
        memcpy(data + first_part, shm_data_area, mem_used_size - first_part);
        g_shared_mem->head = mem_used_size - first_part;
    }


    *data_len = mem_used_size;
    g_shared_mem->used_size = 0;
    pthread_mutex_unlock(&g_shared_mem->mutex);
    return 0;
}